﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SoftwareConsulting.BI11.NumericalMethods.LinearEquationsSystems
{
    /// <summary>
    /// Класс метода Гауса решения СЛАУ
    /// </summary>
    public class GaussMethod
    {
        private const int N = 50;
        private double[] _result;        

        /// <summary>
        /// Конструктор по умолчанию
        /// </summary>
        /// <param name="n">Число уравнений в системе</param>
        /// <param name="coefficients">коэффициенты уравнений системы</param>
        public GaussMethod(int n, double[,] coefficients)
        {
            _result = new double[n];
            double[] x = new double[n];
            int[] res1 = new int[2*n];                        
            for (int i = 0; i < n + 1; i++)
                res1[i] = i;
            //собственно метод Гаусса
            for (int k = 0; k < n; k++)
            {
                CalculateMainElement(k, coefficients, n, res1);
                for (int j = n; j >= k; j--)
                    coefficients[k, j] /= coefficients[k, k];
                for (int i = k + 1; i < n; i++)
                    for (int j = n; j >= k; j--)
                        coefficients[i, j] -= coefficients[k, j] * coefficients[i, k];
            }            
            for (int i = 0; i < n; i++)
                x[i] = coefficients[i, n];
            for (int i = n - 2; i >= 0; i--)
                for (int j = i + 1; j < n; j++)
                    x[i] -= x[j] * coefficients[i, j];            
            int p = 0;
            for (int i = 0; i < n; i++)
                for (int j = 0; j < n; j++)
                    if (i == res1[j])
                    {
                        _result[p] = x[j];
                        p++;
                        break;
                    }
        }        

        /// <summary>
        /// Вычисляет главный элемент
        /// </summary>
        /// <param name="equationIndex">индекс уравнения</param>
        /// <param name="coefficients">коэффициенты СЛАУ</param>
        /// <param name="n">число уравнений</param>
        /// <param name="res1">результат</param>        
        private void CalculateMainElement(int equationIndex, double[,] coefficients, int n, int[] res1)
        {
            int i = 0;
            int j = equationIndex;
            int iMax = equationIndex;
            int jMax = equationIndex;
            double temp = 0;
            //ищем максимальный по модулю элемент
            for (i = equationIndex; i < n; i++)
                for (j = equationIndex; j < n; j++)
                    if (Math.Abs(coefficients[iMax, jMax]) < Math.Abs(coefficients[i, j]))
                    {
                        iMax = i;
                        jMax = j;
                    }
            //меняем строки
            for (j = equationIndex; j < n + 1; j++)
            {
                temp = coefficients[equationIndex, j];
                coefficients[equationIndex, j] = coefficients[iMax, j];
                coefficients[iMax, j] = temp;
            }
            //меняем колонки
            for (i = 0; i < n; i++)
            {
                temp = coefficients[i, equationIndex];
                coefficients[i, equationIndex] = coefficients[i, jMax];
                coefficients[i, jMax] = temp;
            }
            //учитываем смену порядка корней
            i = res1[equationIndex];
            res1[equationIndex] = res1[jMax];
            res1[jMax] = i;
        }
        
        /// <summary>
        /// Возвращает решение
        /// </summary>        
        public double[] GetSolution()
        {
            return _result;
        }
    }
}
